﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Windows.Forms;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace EzyChoice
{
    [Serializable()]
    public class EzcItem :TreeNode, ISerializable
    {
        protected string guid;
        public string ID
        {
            get
            {
                return guid;
            }
        }

        public List<EzcItem> Childs = new List<EzcItem>();
        public Dictionary<Participate, double> ParticipateWeight = new Dictionary<Participate, double>();

        public double weight;

        protected string name;

        public virtual string iName
        {
            get
            {
                return name;
            }

            set
            {
                name = value;
                this.Text = name + " (" + weight + ")";
            }
        }

        public EzcItem()
        {
            this.guid = Guid.NewGuid().ToString();
        }

        public EzcItem(string name)
        {
            this.guid = Guid.NewGuid().ToString();
            this.iName = name;
            //this.Text = name;
        }

        public void RefreshName()
        {
            this.Text = name + " (" + weight + ")";
        }

        public Dictionary<Alternative, double> FlyingScore = new Dictionary<Alternative, double>();

        public void AdJustChilds()
        {
            if(this.Childs.Count>0)
            {
                this.Nodes.Clear();
                this.Nodes.AddRange(Childs.ToArray());
                foreach (EzcItem child in this.Childs)
                {
                    child.AdJustChilds();
                }
            }
        }

        public void AddChild(EzcItem cr)
        {
            this.Childs.Add(cr);
            this.Nodes.Add(cr);
        }

        public void AddChildRange(List<EzcItem> crs)
        {
            this.Childs.AddRange(crs.ToArray());
            this.Nodes.AddRange(crs.ToArray());
        }

        public void RemoveChild(EzcItem cri)
        {
            this.Childs.Remove(cri);
            this.Nodes.Remove(cri);
        }

        public void Clear()
        {
            this.Childs.Clear();
            this.Nodes.Clear();
        }


        public double GetNormalizeWeight(Participate par)
        {
            
            double weight = 0.0;
            if (this.GetType() == typeof(Criteria))
            {
                EzcItem parrent = this.Parent as EzcItem;

                if (parrent != null)
                {
                    double w = GetWeight(par);
                    double s = parrent.GetSumChildWeight(par);
                    weight = s > 0 ? Math.Round(w / s, 3) : 0;
                    this.weight = weight;
                    iName = name;
                }
            }



            return weight;
        }

        public void GenSummaryNormalizeTree(List<Participate> pars, Participate summary)
        {

            foreach (Participate par in pars)
            {
                if (par != summary)
                {
                    GenNormalizeTree(par);
                }
            }

            RecuresiveSummary(pars, summary);
        }

        private void RecuresiveSummary(List<Participate> pars, Participate summary)
        {
            
            foreach (EzcItem c in Childs)
            {
                c.RecuresiveSummary(pars, summary);

                double weight = 0.0;
                int num = 0;
                foreach (Participate par in pars)
                {
                    if (par != summary || par.IsWork)
                    {

                        weight += c.ParticipateWeight[par];
                        num++;
                    }
                    else
                    {
                        c.GetNormalizeWeight(par);
                    }
                }

                c.weight = num > 0 ? Math.Round(weight / num, 3) : 0;
                c.ParticipateWeight[summary] = c.weight;
                c.iName = c.name;
                

            }

            GenNormalizeTree(summary);
        }

        public void GenNormalizeTree(Participate par)
        {
            foreach (EzcItem c in Childs)
            {
                c.GenNormalizeTree(par);
                c.GetNormalizeWeight(par);
            }

            foreach (EzcItem c in Childs)
            {
                c.ParticipateWeight[par] = c.weight;
            }
        }

        public double GetWeight(Participate par)
        {
            double weight = 0.0;

            if (ParticipateWeight.ContainsKey(par))
            {
                weight = ParticipateWeight[par];
            }
            else
            {
                ParticipateWeight.Add(par, weight);
            }

            return weight;
        }

        public double GetSumChildWeight(Participate par)
        {
            double sum = 0.0;
            foreach (EzcItem c in Childs)
            {
                sum += c.GetWeight(par);
            }

            return sum;
        }

        public double CallAlternativeScore(Alternative alt, Participate par)
        {
            double sum = 0.0;

            if (alt.Rating.ContainsKey(this as Criteria) && alt.Rating[this as Criteria].ContainsKey(par))
            {
                sum = alt.Rating[this as Criteria][par].GetNormalizedWeight();
            }


            foreach (Criteria c in this.Childs)
            {
                sum += c.CallAlternativeScore(alt, par) * GetNormalizeWeight(par);
            }

            return sum ;
        }

        public void ConstructScore(List<Alternative> alters, Participate par)
        {
            FlyingScore = new Dictionary<Alternative, double>();

            foreach (Alternative a in alters)
            {
                FlyingScore.Add(a, 0.0);
            }

            foreach (Criteria c in Childs)
            {
                c.ConstructScore(alters, par);
            }

            if ((this as Criteria) != null && (this as Criteria).Choices.Count > 0)
            {
                ConstructChoice(alters, par);
            }

            EzcItem parrent = Parent as EzcItem;

            if (parrent != null)
            {
                foreach (Alternative a in alters)
                {
                    parrent.FlyingScore[a] += Math.Round(  this.FlyingScore[a] * GetNormalizeWeight(par),3);
                }
            }

        }

        

        private void ConstructChoice(List<Alternative> alters, Participate par)
        {
            double sum = 0;

            if (par.IsSummary && !par.IsWork)
            {
                foreach (Alternative a in alters)
                {
                    if (a.Rating.ContainsKey(this as Criteria)  )
                    {
                        Dictionary<Participate,Questionnaire> dic = a.Rating[this as Criteria];

                        if (dic.ContainsKey(par))
                        {
                            dic.Remove(par);
                        }

                        double sm = 0.0;
                        double c = dic.Keys.Count;
                        foreach (Questionnaire qi in dic.Values)
                        {
                            sm += qi.GetNormalizedWeight();
                        }

                        Questionnaire q = new Questionnaire();
                        q.parrent = null;
                        q.weight = c > 0 ? sm / c : 0;

                        dic.Add(par, q);

                    }
                }
            }

            foreach (Alternative a in alters)
            {
                if (a.Rating.ContainsKey(this as Criteria) && a.Rating[this as Criteria].ContainsKey(par))
                {
                    sum += a.Rating[this as Criteria][par].GetNormalizedWeight();
                }
            }

            sum = sum > 0 ? sum : 1;

            foreach (Alternative a in alters)
            {
                if (a.Rating.ContainsKey(this as Criteria) && a.Rating[this as Criteria].ContainsKey(par))
                {
                    FlyingScore[a] = Math.Round(a.Rating[this as Criteria][par].GetNormalizedWeight() / sum, 3);
                }
            }
        }


        protected EzcItem(SerializationInfo info, StreamingContext context)
        {
            this.Childs = (List<EzcItem>)info.GetValue("childs", typeof(List<EzcItem>));
            this.iName = (string)info.GetValue("name", typeof(string));
            this.guid = (string)info.GetValue("guid", typeof(string));
            this.ParticipateWeight = (Dictionary<Participate, double>)info.GetValue("ParticipateWeight", typeof(Dictionary<Participate, double>));
        }

        public virtual void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("childs", this.Childs);
            info.AddValue("name", this.iName);
            info.AddValue("guid", this.guid);
            info.AddValue("ParticipateWeight", this.ParticipateWeight);
        }
    }
}
